﻿using DAL.Interfases;
using Entidad;
using Npgsql;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Text;
using Dapper;
using DapperExtensions;

namespace DAL.Repositories
{
    public interface IFacturaProveedorRepository : IRepository<FacturaProveedor>
    {
        int AddConRemito(FacturaProveedor newEntity);
        IList<FacturaProveedor> FindAllNoAutorizadasConProveedor();
        IList<FacturaProveedor> FindAllConProveedor();
        int AutorizarPago(int factura_id, string observacion);
        FacturaProveedor FindByIdConDetalleProveedor(int id);
        bool EliminarItemDetalle(int id);
        IList<FacturaProveedorRemitoProveedor> FindRemitosFactura(int factura_id);
        bool EliminarRemitoFactura(int remito_id);
        bool ValidarAsociacionRemito(int factura_id, int remito_id);
        
    }

    public class FacturaProveedorRepository : IFacturaProveedorRepository
    {
        readonly NpgsqlConnection _cnn;

        public FacturaProveedorRepository()
        {
            _cnn = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString());
        }

        public IList<FacturaProveedor> FindAll()
        {
            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                _db.Query<FacturaProveedor>("SELECT * FROM FACTURAPROVEEDOR").ToList();
            }
            throw new NotImplementedException();
        }

        public IQueryable<FacturaProveedor> Find(System.Linq.Expressions.Expression<Func<FacturaProveedor, bool>> predicate)
        {
            throw new NotImplementedException();
        }

        public FacturaProveedor FindById(int id)
        {
            string query = "SELECT * FROM FACTURAPROVEEDOR WHERE FAPID = " + id;

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                return _db.Query<FacturaProveedor>(query).SingleOrDefault();
            }
        }

        public int add(FacturaProveedor newEntity)
        {
            string query = "INSERT INTO FACTURAPROVEEDOR(FAPNUMERO, FAPFECHA, FAPFECHARECEPCION, " +
                "PROID, ESTID, FAPIMPORTE, OBSERVACIONES, SUBTOTAL, IVA, INGBRUTOS, AUTORIZADA) " +
                "VALUES (@numero, @fecha, @fecharecepcion, @proveedor, " +
                "@estado, @importe, @observaciones, @subtotal, @iva, @ingbrutos, @autorizada);" +
                "SELECT CURRVAL('facturaproveedor_fapid_seq');";

            string queryDetalle = "INSERT INTO FACTURAPROVEEDORDETALLE (FAPID, PRDID, FPDIMPORTEUNIT, " +
                "FPDCANTIDAD, ODCID) VALUES (@factura_id, @producto_id, @importeunit, @cantidad, @orden_id)";

            string queryFacturaRemito = "INSERT INTO FACTURAPROVEEDOR_REMITOPROVEEDOR (FACTURAPROVEEDOR_ID, REMITOPROVEEDOR_ID) " +
                "VALUES (@factura, @remito)";

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                _db.Open();               
                IDbTransaction transaction = _db.BeginTransaction();

                try
                {      
                    //inserto el encabezado de la factura
                    int resultado = _db.Query<int>(query, new
                    {
                        numero = newEntity.numero,
                        fecha = newEntity.fecha,
                        fecharecepcion = newEntity.fecharecepcion,                      
                        proveedor = newEntity.proveedor_id,
                        estado = 1,
                        importe = newEntity.importe,
                        observaciones = newEntity.observaciones,
                        subtotal = newEntity.subtotal,
                        iva = newEntity.iva,
                        ingbrutos = newEntity.ingbrutos,
                        autorizada = newEntity.autorizada
                    }).Single();

                    if(newEntity.autorizada == 1)
                    {
                        string queryPago = "INSERT INTO FACTURAPROVEEDOR_AUTORIZACIONPAGO " +
                            "(FACTURAPROVEEDOR_ID, FECHA, OBSERVACION) VALUES (@FACTURAID, @FECHA, @OBSERVACION)";

                        _db.Execute(queryPago, new {facturaid = resultado, fecha = DateTime.Today.Date, observacion = newEntity.observaciones });
                    }

                    //inserto los remitos vinculados a la factura
                    foreach(RemitoProveedor remito in newEntity.remitos)
                    {
                         _db.Execute(queryFacturaRemito, new { factura = resultado, remito = remito.id});
                    }

                    //inserto el detalle de la factura
                    foreach (FacturaProveedorDetalle row in newEntity.detalle)
                    {
                        _db.Execute(queryDetalle, new
                        {
                            factura_id = resultado,
                            producto_id = row.prdid,
                            importeunit = row.fpdimporteunit,
                            cantidad = row.fpdcantidad,
                            orden_id = row.odcid
                        });
                    }

                    transaction.Commit();
                    return resultado;

                }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    throw ex;
                }

            }
        }

        public bool remove(int id)
        {
            throw new NotImplementedException();
        }

        public int Modify(FacturaProveedor entity)
        {
            string query = "UPDATE FACTURAPROVEEDOR SET FAPNUMERO = @numero, FAPFECHA = @fecha, FAPFECHARECEPCION = @fecharecepcion, " +
                "FAPIMPORTE = @importe, OBSERVACIONES = @observaciones, SUBTOTAL = @subtotal, IVA = @iva, INGBRUTOS = @ingbrutos, " +
                "AUTORIZADA = @autorizada WHERE FAPID = " + entity.id;

            string queryDetalle = "INSERT INTO FACTURAPROVEEDORDETALLE (FAPID, PRDID, FPDIMPORTEUNIT, " +
                "FPDCANTIDAD, ODCID) VALUES (@factura_id, @producto_id, @importeunit, @cantidad, @orden_id)";

            string queryFacturaRemito = "INSERT INTO FACTURAPROVEEDOR_REMITOPROVEEDOR (FACTURAPROVEEDOR_ID, REMITOPROVEEDOR_ID) " +
               "VALUES (@factura, @remito)";

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                _db.Open();
                IDbTransaction transaction = _db.BeginTransaction();

                int resultado = _db.Execute(query, new
                {
                    numero = entity.numero,
                    fecha = entity.fecha,
                    fecharecepcion = entity.fecharecepcion,
                    importe = entity.importe,
                    observaciones = entity.observaciones,
                    subtotal = entity.subtotal,
                    iva = entity.iva,
                    ingbrutos = entity.ingbrutos,
                    autorizada = entity.autorizada
                });

                //modifico el detalle
                foreach(FacturaProveedorDetalle fila in entity.detalle)
                {
                    if(fila.fpdid == 0)
                    {
                        _db.Execute(queryDetalle, new { 
                            factura_id = entity.id,
                            producto_id = fila.prdid, 
                            importeunit = fila.fpdimporteunit, 
                            cantidad = fila.fpdcantidad, 
                            orden_id = fila.odcid}); 
                    }
                }

                foreach(RemitoProveedor row in entity.remitos)
                {
                    if(ValidarAsociacionRemito(entity.id, row.id) == false)
                    {
                        _db.Execute(queryFacturaRemito, new { factura = entity.id, remito = row.id });
                    }
                }

                transaction.Commit();
                return resultado;
            }
        }



        public int AddConRemito(FacturaProveedor newEntity)
        {
            string query = "INSERT INTO FACTURAPROVEEDOR(FAPNUMERO, FAPFECHA, FAPFECHARECEPCION, " +
               "PROID, ESTID, FAPIMPORTE, OBSERVACIONES, SUBTOTAL, IVA, INGBRUTOS, AUTORIZADA) " +
               "VALUES (@numero, @fecha, @fecharecepcion, @proveedor, " +
               "@estado, @importe, @observaciones, @subtotal, @iva, @ingbrutos, @autorizada);" +
               "SELECT CURRVAL('facturaproveedor_fapid_seq');";

            string queryDetalle = "INSERT INTO FACTURAPROVEEDORDETALLE (FAPID, PRDID, FPDIMPORTEUNIT, " +
                "FPDCANTIDAD, ODCID) VALUES (@factura_id, @producto_id, @importeunit, @cantidad, @orden_id)";

            string queryFacturaRemito = "INSERT INTO FACTURAPROVEEDOR_REMITOPROVEEDOR (FACTURAPROVEEDOR_ID, REMITOPROVEEDOR_ID) " +
                "VALUES (@factura, @remito)";

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                _db.Open();
                IDbTransaction transaction = _db.BeginTransaction();

                try
                {
                    //inserto el encabezado de la factura
                    int resultado = _db.Query<int>(query, new
                    {
                        numero = newEntity.numero,
                        fecha = newEntity.fecha,
                        fecharecepcion = newEntity.fecharecepcion,
                        proveedor = newEntity.proveedor_id,
                        estado = 1,
                        importe = newEntity.importe,
                        observaciones = newEntity.observaciones,
                        subtotal = newEntity.subtotal,
                        iva = newEntity.iva,
                        ingbrutos = newEntity.ingbrutos,
                        autorizada = newEntity.autorizada
                    }).Single();


                    if (newEntity.autorizada == 1)
                    {
                        string queryPago = "INSERT INTO FACTURAPROVEEDOR_AUTORIZACIONPAGO " +
                            "(FACTURAPROVEEDOR_ID, FECHA, OBSERVACION) VALUES (@FACTURAID, @FECHA, @OBSERVACION)";

                        _db.Execute(queryPago, new { facturaid = resultado, fecha = DateTime.Today.Date, observacion = newEntity.observaciones });
                    }

                    //inserto los remitos vinculados a la factura
                    //foreach (RemitoProveedor remito in newEntity.remitos)
                    //{
                    //    _db.Execute(queryFacturaRemito, new { factura = resultado, remito = remito.id });
                    //}

                    //inserto el detalle de la factura
                    List<RemitoProveedorDetalle> detalleRemito = new List<RemitoProveedorDetalle>();
                    foreach (FacturaProveedorDetalle row in newEntity.detalle)
                    {

                        RemitoProveedorDetalle detRemito = new RemitoProveedorDetalle();
                        detRemito.orden_id = row.odcid;
                        detRemito.producto_id = row.prdid;
                        detRemito.sucursal_id = row.sucid;
                        detRemito.cantidad = row.fpdcantidad;

                        detalleRemito.Add(detRemito);
                        
                        _db.Execute(queryDetalle, new
                        {
                            factura_id = resultado,
                            producto_id = row.prdid,
                            importeunit = row.fpdimporteunit,
                            cantidad = row.fpdcantidad,
                            orden_id = row.odcid
                        });
                    }                                  

                    RemitoProveedor remitoProveedor = new RemitoProveedor();
                    remitoProveedor.activo = 1;
                    remitoProveedor.fechaemision = newEntity.fecha;
                    remitoProveedor.fecharecepcion = newEntity.fecharecepcion;
                    remitoProveedor.numero = newEntity.numeroremito;
                    remitoProveedor.observaciones = newEntity.observaciones;
                    remitoProveedor.proveedor_id = newEntity.proveedor_id;
                    remitoProveedor.sucursal_id = newEntity.sucursal_id;
                    remitoProveedor.numero = newEntity.numeroremito;
                    remitoProveedor.detalle = detalleRemito;

                    IRemitoProveedorRepository _repository = new RemitoProveedorRepository();
                    int remito_id = _repository.add(remitoProveedor);

                    _db.Execute(queryFacturaRemito, new { factura = resultado, remito = remito_id });

                    transaction.Commit();
                    return resultado;

                }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    throw ex;
                }

            }
        }


        public IList<FacturaProveedor> FindAllNoAutorizadasConProveedor()
        {
            string query = "SELECT * FROM FACTURAPROVEEDOR " +
                "INNER JOIN PROVEEDOR ON PROVEEDOR.PROID = FACTURAPROVEEDOR.PROID " +
                "WHERE FACTURAPROVEEDOR.AUTORIZADA = 0 " +
                "ORDER BY FACTURAPROVEEDOR.FAPFECHA DESC";

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                return _db.Query<FacturaProveedor, Proveedor, FacturaProveedor>(query, 
                    (factura, proveedor) => 
                    {factura.proveedor = proveedor;
                    return factura;
                    }, splitOn: "proid").ToList();
            }
        }


        public IList<FacturaProveedor> FindAllConProveedor()
        {
            string query = "SELECT * FROM FACTURAPROVEEDOR " +
              "INNER JOIN PROVEEDOR ON PROVEEDOR.PROID = FACTURAPROVEEDOR.PROID " +
              "ORDER BY FACTURAPROVEEDOR.FAPFECHA DESC";

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                return _db.Query<FacturaProveedor, Proveedor, FacturaProveedor>(query,
                    (factura, proveedor) =>
                    {
                        factura.proveedor = proveedor;
                        return factura;
                    }, splitOn: "proid").ToList();
            }
        }


        public int AutorizarPago(int factura_id, string observacion)
        {
            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                string queryPago = "INSERT INTO FACTURAPROVEEDOR_AUTORIZACIONPAGO " +
                           "(FACTURAPROVEEDOR_ID, FECHA, OBSERVACION) VALUES (@FACTURAID, @FECHA, @OBSERVACION)";

                string queryFactura = "UPDATE FACTURAPROVEEDOR SET AUTORIZADA = 1 " +
                    "WHERE FAPID = " + factura_id;

                int resultado = _db.Execute(queryFactura);

                _db.Execute(queryPago, new { facturaid = factura_id, fecha = DateTime.Today.Date, observacion = observacion });

                return resultado;
            }
        }


        public FacturaProveedor FindByIdConDetalleProveedor(int id)
        {
            string query = "SELECT * FROM FACTURAPROVEEDOR " +
                "INNER JOIN PROVEEDOR ON PROVEEDOR.PROID = FACTURAPROVEEDOR.PROID " +
                "WHERE FACTURAPROVEEDOR.FAPID = " + id;

            string queryDetalle = "SELECT * FROM FACTURAPROVEEDORDETALLE " + 
                "INNER JOIN PRODUCTO ON PRODUCTO.PRDID = FACTURAPROVEEDORDETALLE.PRDID " +
                "LEFT JOIN ORDENCOMPRA ON ORDENCOMPRA.ODCID = FACTURAPROVEEDORDETALLE.ODCID " +                
                "WHERE FACTURAPROVEEDORDETALLE.FAPID = " + id;

            string queryRemitos = "SELECT * FROM FACTURAPROVEEDOR_REMITOPROVEEDOR " +
                "WHERE FACTURAPROVEEDOR_ID = " + id;

            FacturaProveedor factura = new FacturaProveedor();

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                factura = _db.Query<FacturaProveedor, Proveedor, FacturaProveedor>(query,
                    (fact, proveedor) => { 
                        fact.proveedor = proveedor; 
                        return fact; }, splitOn: "proid").SingleOrDefault();

                factura.detalle = _db.Query<FacturaProveedorDetalle, Producto, OrdenCompra, FacturaProveedorDetalle>
                    (queryDetalle, (detalle, producto, orden) => 
                    { detalle.producto = producto; 
                        detalle.orden = orden; 
                        return detalle; }, 
                        splitOn: "prdid, odcid").ToList();               
            }

            return factura;
        }


        public bool EliminarItemDetalle(int id)
        {
            bool res = true;
            string query = "DELETE FROM FACTURAPROVEEDORDETALLE WHERE FPDID = " + id;
            
            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                int resultado = _db.Execute(query);
                if (resultado == 0 || resultado < 0)
                    res = false;          
            }

            return res;
        }

        public IList<FacturaProveedorRemitoProveedor> FindRemitosFactura(int factura_id)
        {
            string query = "SELECT * FROM FACTURAPROVEEDOR_REMITOPROVEEDOR " +
                "INNER JOIN FACTURAPROVEEDOR ON FACTURAPROVEEDOR.FAPID = FACTURAPROVEEDOR_REMITOPROVEEDOR.FACTURAPROVEEDOR_ID " + 
                "INNER JOIN REMITOPROVEEDOR ON REMITOPROVEEDOR.REMITOPROVEEDOR_ID = FACTURAPROVEEDOR_REMITOPROVEEDOR.REMITOPROVEEDOR_ID " +
               "WHERE FACTURAPROVEEDOR_REMITOPROVEEDOR.FACTURAPROVEEDOR_ID = " + factura_id;

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                return _db.Query<FacturaProveedorRemitoProveedor, FacturaProveedor, RemitoProveedor, FacturaProveedorRemitoProveedor>(query,
                    (remitos, factura, remito) => { 
                        remitos.factura = factura; 
                        remitos.remito = remito; 
                        return remitos; }, splitOn: "remitoproveedor_id").ToList();
            }
        }


      

        public bool EliminarRemitoFactura(int remito_id)
        {
            bool res = true;
            string query = "DELETE FROM FACTURAPROVEEDOR_REMITOPROVEEDOR " +
                "WHERE REMITOPROVEEDOR_ID = " + remito_id;

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                int resultado = _db.Execute(query);
                if (resultado == 0 || resultado < 0)
                    res = false;
            }

            return res;
        }

    
        public bool ValidarAsociacionRemito(int factura_id, int remito_id)
        {
            bool res = true;
            string query = "SELECT COUNT(*) FROM FACTURAPROVEEDOR_REMITOPROVEEDOR " +
                "WHERE FACTURAPROVEEDOR_ID = " + factura_id + " AND REMITOPROVEEDOR_ID = " + remito_id;

            using (IDbConnection _db = new NpgsqlConnection(ConfigurationManager.ConnectionStrings["RWORLD"].ToString()))
            {
                int resultado = _db.Query<int>(query).Single();
                if (resultado <= 0)
                    res = false;
            }
            return res;
        }

       
    }
}
